/*
 * ZooInspector
 * 
 * Copyright 2010 Colin Goodheart-Smithe

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.apache.zookeeper.inspector.gui;

import info.clearthought.layout.TableLayout;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Frame;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer;
import org.apache.zookeeper.inspector.logger.LoggerFactory;
import org.apache.zookeeper.inspector.manager.ZooInspectorManager;

/**
 * @author CGSmithe
 * 
 */
public class ZooInspectorNodeViewersDialog extends JDialog implements ListSelectionListener
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1273699403814184341L;
	private final JButton upButton;
	private final JButton downButton;
	private final JButton removeButton;
	private final JButton addButton;
	private final JList<ZooInspectorNodeViewer> viewersList;
	private final JButton saveFileButton;
	private final JButton loadFileButton;
	private final JButton setDefaultsButton;
	private final JFileChooser fileChooser = new JFileChooser(new File("."));

	/**
	 * @param frame
	 * @param currentViewers
	 * @param listeners
	 * @param manager
	 * 
	 */
	public ZooInspectorNodeViewersDialog(Frame frame,
			final List<ZooInspectorNodeViewer> currentViewers,
			final Collection<NodeViewersChangeListener> listeners, final ZooInspectorManager manager)
	{
		super(frame);
		final List<ZooInspectorNodeViewer> newViewers = new ArrayList<ZooInspectorNodeViewer>(
				currentViewers);
		this.setLayout(new BorderLayout());
		this.setIconImage(ZooInspectorIconResources.getChangeNodeViewersIcon().getImage());
		this.setTitle("页面管理");
		this.setModal(true);
		this.setAlwaysOnTop(true);
		this.setResizable(true);
		final JPanel panel = new JPanel();
		panel.setLayout(new TableLayout(new double[] { 10, TableLayout.PREFERRED, 5,
				TableLayout.PREFERRED, 5, TableLayout.FILL, TableLayout.PREFERRED, 5,
				TableLayout.PREFERRED, 10 }, new double[] { 10, TableLayout.PREFERRED, 5,
				TableLayout.PREFERRED, 5, TableLayout.PREFERRED, TableLayout.FILL, 5,
				TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 10 }));
		viewersList = new JList<ZooInspectorNodeViewer>();
		DefaultListModel<ZooInspectorNodeViewer> model = new DefaultListModel<ZooInspectorNodeViewer>();
		for (ZooInspectorNodeViewer viewer : newViewers)
		{
			model.addElement(viewer);
		}
		viewersList.setModel(model);
		viewersList.setCellRenderer(new DefaultListCellRenderer()
		{
			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public Component getListCellRendererComponent(JList<?> list, Object value, int index,
					boolean isSelected, boolean cellHasFocus)
			{
				ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) value;
				JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index,
						isSelected, cellHasFocus);
				label.setText(viewer.getTitle());
				return label;
			}
		});
		viewersList.setDropMode(DropMode.INSERT);
		viewersList.enableInputMethods(true);
		viewersList.setDragEnabled(true);
		viewersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		viewersList.getSelectionModel().addListSelectionListener(this);
		viewersList.setTransferHandler(new TransferHandler()
		{

			/**
			 * 
			 */
			private static final long serialVersionUID = 1L;

			@Override
			public boolean canImport(TransferHandler.TransferSupport info)
			{
				// we only import NodeViewers
				if (!info.isDataFlavorSupported(ZooInspectorNodeViewer.nodeViewerDataFlavor))
				{
					return false;
				}

				JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
				if (dl.getIndex() == -1)
				{
					return false;
				}
				return true;
			}

			@Override
			public boolean importData(TransferHandler.TransferSupport info)
			{
				JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
				DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
				int index = dl.getIndex();
				boolean insert = dl.isInsert();
				// Get the string that is being dropped.
				Transferable t = info.getTransferable();
				String data;
				try
				{
					data = (String) t.getTransferData(ZooInspectorNodeViewer.nodeViewerDataFlavor);
				}
				catch (Exception e)
				{
					return false;
				}
				try
				{
					ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class.forName(data)
							.newInstance();
					if (listModel.contains(viewer))
					{
						listModel.removeElement(viewer);
					}
					if (insert)
					{
						listModel.add(index, viewer);
					}
					else
					{
						listModel.set(index, viewer);
					}
					return true;
				}
				catch (Exception e)
				{
					LoggerFactory.getLogger().error("Error instantiating class: " + data, e);
					return false;
				}

			}

			@Override
			public int getSourceActions(JComponent c)
			{
				return MOVE;
			}

			@Override
			protected Transferable createTransferable(JComponent c)
			{
				JList<?> list = (JList<?>) c;
				ZooInspectorNodeViewer value = (ZooInspectorNodeViewer) list.getSelectedValue();
				return value;
			}
		});
		JScrollPane scroller = new JScrollPane(viewersList);
		panel.add(scroller, "1,1,6,6");
		upButton = new JButton(ZooInspectorIconResources.getUpIcon());
		downButton = new JButton(ZooInspectorIconResources.getDownIcon());
		removeButton = new JButton(ZooInspectorIconResources.getDeleteNodeIcon());
		addButton = new JButton(ZooInspectorIconResources.getAddNodeIcon());
		upButton.setEnabled(false);
		downButton.setEnabled(false);
		removeButton.setEnabled(false);
		addButton.setEnabled(true);
		upButton.setToolTipText("向上移动视图");
		downButton.setToolTipText("向下移动视图");
		removeButton.setToolTipText("移除当前选中的视图");
		addButton.setToolTipText("增加视图");
		final JTextField newViewerTextField = new JTextField();
		panel.add(upButton, "8,1");
		panel.add(downButton, "8,5");
		panel.add(removeButton, "8,3");
		panel.add(newViewerTextField, "1,8,6,8");
		panel.add(addButton, "8,8");
		upButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
				ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList
						.getSelectedValue();
				int index = viewersList.getSelectedIndex();
				if (listModel.contains(viewer))
				{
					listModel.removeElementAt(index);
					listModel.insertElementAt(viewer, index - 1);
					viewersList.setSelectedValue(viewer, true);
				}
			}
		});
		downButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
				ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList
						.getSelectedValue();
				int index = viewersList.getSelectedIndex();
				if (listModel.contains(viewer))
				{
					listModel.removeElementAt(index);
					listModel.insertElementAt(viewer, index + 1);
					viewersList.setSelectedValue(viewer, true);
				}
			}
		});
		removeButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
				ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList
						.getSelectedValue();
				int index = viewersList.getSelectedIndex();
				if (listModel.contains(viewer))
				{
					listModel.removeElement(viewer);
					viewersList.setSelectedIndex(index == listModel.size() ? index - 1 : index);
				}
			}
		});
		addButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				String className = newViewerTextField.getText();
				if (className == null || className.length() == 0)
				{
					JOptionPane
							.showMessageDialog(
									ZooInspectorNodeViewersDialog.this,
									"Please enter the full class name for a Node Viewer and click the add button",
									"Input Error", JOptionPane.ERROR_MESSAGE);
				}
				else
				{
					try
					{
						DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
						ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class.forName(
								className).newInstance();
						if (listModel.contains(viewer))
						{
							JOptionPane
									.showMessageDialog(
											ZooInspectorNodeViewersDialog.this,
											"Node viewer already exists.  Each node viewer can only be added once.",
											"Input Error", JOptionPane.ERROR_MESSAGE);
						}
						else
						{
							listModel.addElement(viewer);
						}
					}
					catch (Exception ex)
					{
						LoggerFactory.getLogger().error(
								"An error occurred while instaniating the node viewer. ", ex);
						JOptionPane.showMessageDialog(ZooInspectorNodeViewersDialog.this,
								"初始化视图失败: "
										+ ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
					}
				}
			}
		});
		saveFileButton = new JButton("保存");
		loadFileButton = new JButton("加载");
		setDefaultsButton = new JButton("默认");
		saveFileButton.setToolTipText("持久化视图信息");
		loadFileButton.setToolTipText("加载已持久化的视图信息");
		setDefaultsButton.setToolTipText("保存当前视图配置");
		panel.add(saveFileButton, "1,10");
		panel.add(loadFileButton, "3,10");
		panel.add(setDefaultsButton, "6,10");
		saveFileButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				int result = fileChooser.showSaveDialog(ZooInspectorNodeViewersDialog.this);
				if (result == JFileChooser.APPROVE_OPTION)
				{
					File selectedFile = fileChooser.getSelectedFile();
					int answer = JOptionPane.YES_OPTION;
					if (selectedFile.exists())
					{
						answer = JOptionPane.showConfirmDialog(ZooInspectorNodeViewersDialog.this,
								"指定的文件已存在，是否覆盖该文件?",
								"提示", JOptionPane.YES_NO_OPTION,
								JOptionPane.WARNING_MESSAGE);
					}
					if (answer == JOptionPane.YES_OPTION)
					{
						DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
						List<String> nodeViewersClassNames = new ArrayList<String>();
						Object[] modelContents = listModel.toArray();
						for (Object o : modelContents)
						{
							nodeViewersClassNames.add(((ZooInspectorNodeViewer) o).getClass()
									.getCanonicalName());
						}
						try
						{
							manager.saveNodeViewersFile(selectedFile, nodeViewersClassNames);
						}
						catch (IOException ex)
						{
							LoggerFactory.getLogger().error(
									"Error saving node veiwer configuration from file.", ex);
							JOptionPane.showMessageDialog(ZooInspectorNodeViewersDialog.this,
									"视图信息保存失败: "
											+ ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
						}
					}
				}
			}
		});
		loadFileButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				int result = fileChooser.showOpenDialog(ZooInspectorNodeViewersDialog.this);
				if (result == JFileChooser.APPROVE_OPTION)
				{
					try
					{
						List<String> nodeViewersClassNames = manager
								.loadNodeViewersFile(fileChooser.getSelectedFile());
						List<ZooInspectorNodeViewer> nodeViewers = new ArrayList<ZooInspectorNodeViewer>();
						for (String nodeViewersClassName : nodeViewersClassNames)
						{
							ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class.forName(
									nodeViewersClassName).newInstance();
							nodeViewers.add(viewer);
						}
						DefaultListModel<ZooInspectorNodeViewer> model = new DefaultListModel<ZooInspectorNodeViewer>();
						for (ZooInspectorNodeViewer viewer : nodeViewers)
						{
							model.addElement(viewer);
						}
						viewersList.setModel(model);
						panel.revalidate();
						panel.repaint();
					}
					catch (Exception ex)
					{
						LoggerFactory.getLogger().error(
								"Error loading node veiwer configuration from file.", ex);
						JOptionPane.showMessageDialog(ZooInspectorNodeViewersDialog.this,
								"视图加载失败: "
										+ ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
					}
				}
			}
		});
		setDefaultsButton.addActionListener(new ActionListener()
		{

			public void actionPerformed(ActionEvent e)
			{
				int answer = JOptionPane.showConfirmDialog(ZooInspectorNodeViewersDialog.this,
						"确定将当前视图配置设置为默认值?",
						"提示", JOptionPane.YES_NO_OPTION,
						JOptionPane.WARNING_MESSAGE);
				if (answer == JOptionPane.YES_OPTION)
				{
					DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
					List<String> nodeViewersClassNames = new ArrayList<String>();
					Object[] modelContents = listModel.toArray();
					for (Object o : modelContents)
					{
						nodeViewersClassNames.add(((ZooInspectorNodeViewer) o).getClass()
								.getCanonicalName());
					}
					try
					{
						manager.setDefaultNodeViewerConfiguration(nodeViewersClassNames);
					}
					catch (IOException ex)
					{
						LoggerFactory.getLogger().error(
								"Error setting default node veiwer configuration.", ex);
						JOptionPane.showMessageDialog(ZooInspectorNodeViewersDialog.this,
								"Error setting default node veiwer configuration: "
										+ ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
					}
				}
			}
		});

		JPanel buttonsPanel = new JPanel();
		buttonsPanel.setLayout(new TableLayout(new double[] { 10, TableLayout.FILL,
				TableLayout.PREFERRED, 5, TableLayout.PREFERRED, 10, TableLayout.FILL },
				new double[] { TableLayout.PREFERRED }));
		JButton okButton = new JButton("确定");
		okButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				ZooInspectorNodeViewersDialog.this.dispose();
				DefaultListModel<ZooInspectorNodeViewer> listModel = (DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel();
				newViewers.clear();
				Object[] modelContents = listModel.toArray();
				for (Object o : modelContents)
				{
					newViewers.add((ZooInspectorNodeViewer) o);
				}
				currentViewers.clear();
				currentViewers.addAll(newViewers);
				for (NodeViewersChangeListener listener : listeners)
				{
					listener.nodeViewersChanged(currentViewers);
				}
			}
		});
		buttonsPanel.add(okButton, "2,0");
		JButton cancelButton = new JButton("Cancel");
		cancelButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				ZooInspectorNodeViewersDialog.this.dispose();
			}
		});
		buttonsPanel.add(cancelButton, "4,0");
		this.add(panel, BorderLayout.CENTER);
		this.add(buttonsPanel, BorderLayout.SOUTH);
		this.pack();
	}

	public void valueChanged(ListSelectionEvent e)
	{
		int index = viewersList.getSelectedIndex();
		if (index == -1)
		{
			removeButton.setEnabled(false);
			upButton.setEnabled(false);
			downButton.setEnabled(false);
		}
		else
		{
			removeButton.setEnabled(true);
			if (index == 0)
			{
				upButton.setEnabled(false);
			}
			else
			{
				upButton.setEnabled(true);
			}
			if (index == ((DefaultListModel<ZooInspectorNodeViewer>) viewersList.getModel()).getSize())
			{
				downButton.setEnabled(false);
			}
			else
			{
				downButton.setEnabled(true);
			}
		}
	}
}
